using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using Microsoft.StylusInput;
using Microsoft.StylusInput.PluginData;
using System.Drawing.Imaging;

namespace Woz
{
    /// <summary>
    /// the select tool class for selecting entities on the canvas
    /// </summary>
    public class SelectTool : Woz.Tool
    {
        /// <summary>
        /// default constructor
        /// </summary>
        /// <param name="pCanvasControl">the canvas to draw images on</param>
        public SelectTool(Control pCanvasControl)
            : base(pCanvasControl)
        {
            // initialize the selection pen
            this.mSelectionPen = Woz.ResourceManager.Instance.GetPen(Color.Black, 1.0f);

            // initialize the outline pen
            this.mOutlinePen = Woz.ResourceManager.Instance.GetPen(Color.FromArgb(132, 142, 222), 1.0f);

            // initialize the current screen
            this.mCurrentScreen = Woz.ScreenManager.Instance.SelectedScreen;

            // register all the callbacks
            this.RegisterCallbacks();

            this.mValidModes.Add(Woz.StaticVariables.Instance.DesignScreens);
        }

        /// <summary>
        /// the name of the tool
        /// </summary>
        public override string Name
        {
            get
            {
                return "Select";
            }
        }

        /// <summary>
        /// returns the name of the icon file
        /// </summary>
        public override string IconFileName
        {
            get
            {
                return "icon_cursor.png";
            }
        }

        /// <summary>
        /// gets the stylus events this tool is subscribed to
        /// </summary>
        public override DataInterestMask DataInterest
        {
            get
            {
                return base.DataInterest;
            }
        }

        protected override void OnProcessSelection()
        {
            // subscribe to the delete button
            Woz.InterfaceManager.Instance.Buttons["Delete"].Click += this.cInterfaceManagerButtonClicked;
        }

        protected override void OnProcessDeselection()
        {
            // get a list of all the selected entities
            Woz.Entity[] selectedEntities = Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities;

            // if the list has items in it
            if (selectedEntities.Length > 0)
            {
                // create an empty list of entities
                Woz.Entity[] emtpyEntities = new Woz.Entity[0];

                // then deselect these items
                Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.SelectAction),
                    Woz.ScreenManager.Instance.SelectedScreen, selectedEntities, emtpyEntities);
                action.Execute();
            }

            // unsubscribe from the delete button
            Woz.InterfaceManager.Instance.Buttons["Delete"].Click -= this.cInterfaceManagerButtonClicked;
        }

        /// <summary>
        /// reset the select tool
        /// </summary>
        public override void Reset()
        {
        }

        protected override void OnStylusDown(Point pLocation)
        {
            // reset the stylus moved
            this.mStylusMoved = false;

            // save the starting point
            this.mStartingPoint = pLocation;

            // save the last point as well
            this.mLastPoint = pLocation;

            // check to see if a hittest should be peformed
            Woz.Entity entity = null;
            if (Woz.DynamicVariables.Instance.AlwaysPerformHitTest)
            {
                // peform the hittest
                entity = Woz.ScreenManager.Instance.SelectedScreen.HitTest(pLocation);
            }

            // get a list of the selected entities
            Woz.Entity[] selectedEntities = Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities;

            // check to see if we clicked within the selection box
            if (Woz.ScreenManager.Instance.SelectedScreen.SelectedEntitiesBoundingBox.Contains(pLocation))
            {
                // if we did then we do something with it
                entity = selectedEntities[0];
            }

            // check to see if an entity was found
            if (entity != null)
            {
                // if were performing hittest on the unselected entities then make sure
                // a proper select action is committed
                if (Woz.DynamicVariables.Instance.AlwaysPerformHitTest)
                {
                    // check to see if the entity is selected
                    bool selected = false;
                    for (int i = 0; i < selectedEntities.Length; i++)
                    {
                        if (entity == selectedEntities[i])
                        {
                            selected = true;
                            break;
                        }
                    }

                    // commit a selection action
                    if (!selected)
                    {
                        Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.SelectAction),
                            Woz.ScreenManager.Instance.SelectedScreen, selectedEntities, new Woz.Entity[1] { entity });
                        action.Execute();
                    }
                }

                // we will not be drawing a selection box if an entity is found
                this.mIsDrawingSelectionBox = false;

                // set the selection rect to an empty rect
                this.mSelectionRect = Rectangle.Empty;

                // save the bouding bix of the selected entities
                this.mOriginalSelectedEntitiesRect = Woz.ScreenManager.Instance.SelectedScreen.SelectedEntitiesBoundingBox;
            }
            else
            {
                // we will be drawing a selection box if an entity was not found
                this.mIsDrawingSelectionBox = true;

                // initialize the selection rectangle
                this.mSelectionRect = new Rectangle(pLocation, Size.Empty);
            }
        }

        protected override void OnPackets(Point[] pLocations)
        {
            // calculate one pixel in world space
            int onePixel = Woz.InkSpaceConverter.Instance.InkSpaceOnePixel;

            // set stylus moved to true if the stylus moved more than a pixel
            if ((Math.Abs(this.mStartingPoint.X - pLocations[0].X) >= onePixel) ||
                (Math.Abs(this.mStartingPoint.Y - pLocations[0].Y) >= onePixel))
            {
                this.mStylusMoved = true;
            }

            // if were not moving then leave this function immidiately
            if (!this.mStylusMoved)
            {
                return;
            }

            // if were still drawing selection box
            if (this.mIsDrawingSelectionBox)
            {
                // save the old selection rectangle
                Rectangle oldSelectionRect = this.mSelectionRect;

                // generate the new selection rectangle
                Rectangle selectionRect = Rectangle.FromLTRB(
                    Math.Min(pLocations[0].X, this.mStartingPoint.X),
                    Math.Min(pLocations[0].Y, this.mStartingPoint.Y),
                    Math.Max(pLocations[0].X, this.mStartingPoint.X),
                    Math.Max(pLocations[0].Y, this.mStartingPoint.Y));

                // update the selection rectangle
                this.mSelectionRect = selectionRect;

                // increase the selection rectangle by about ten pixels
                selectionRect.Inflate(Woz.InkSpaceConverter.Instance.InkSpaceTenPixels, Woz.InkSpaceConverter.Instance.InkSpaceTenPixels);
                //selectionRect.Inflate(100, 100);

                // invalidate the combined selection rectangles
                base.InvalidateRect(Rectangle.Union(oldSelectionRect, selectionRect));
            }

            // if were not drawing a selection box then we are moving entities
            else if(!this.mHasLockedEntity)
            {
                // calculate the amount the selection was moved
                Point delta = new Point(pLocations[0].X - this.mLastPoint.X, pLocations[0].Y - this.mLastPoint.Y);

                // if there was no change at all then ignore this packet
                if ((delta.X == 0) && (delta.Y == 0))
                {
                    return;
                }

                // initialize left right top and bottom to caculate bounding box
                int left, right, top, bottom;
                left = right = pLocations[0].X;
                top = bottom = pLocations[0].Y;

                // move all the selected items by this delta
                foreach (Woz.Entity entity in Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities)
                {
                    // only translate the unlocked entities
                    if (!entity.Locked)
                    {
                        // translate (or scale) the entity
                        switch (this.mMotionType)
                        {
                            case MotionType.Movement:
                                entity.Translate(delta.X, delta.Y);
                                break;
                            case MotionType.ScaleTopLeft:
                                entity.Translate(delta.X, delta.Y);
                                entity.Scale(-delta.X, -delta.Y);
                                break;
                            case MotionType.ScaleTopRight:
                                entity.Translate(0, delta.Y);
                                entity.Scale(delta.X, -delta.Y);
                                break;
                            case MotionType.ScaleBottomRight:
                                entity.Scale(+delta.X, +delta.Y);
                                break;
                            case MotionType.ScaleBottomLeft:
                                entity.Translate(delta.X, 0);
                                entity.Scale(-delta.X, +delta.Y);
                                break;
                        }
                    }

                    // compute bounding box
                    left = Math.Min(left, entity.BoundingBox.Left);
                    right = Math.Max(right, entity.BoundingBox.Right);
                    top = Math.Min(top, entity.BoundingBox.Top);
                    bottom = Math.Max(bottom, entity.BoundingBox.Bottom);
                }

                // update the last point
                this.mLastPoint = pLocations[0];

                // create a rectangle to invalidate to show the movement
                Rectangle invalidRect = Rectangle.FromLTRB(left, top, right, bottom);

                // set the selection rect to the invalid rect
                this.mSelectionRect = invalidRect;

                // inflate it by about ten pixels to account for the selection
                invalidRect.Inflate(Math.Abs(delta.X) + Woz.InkSpaceConverter.Instance.InkSpaceTenPixels,
                    Math.Abs(delta.Y) + Woz.InkSpaceConverter.Instance.InkSpaceTenPixels);

                // invalidate the invalid rectangle
                base.InvalidateRect(invalidRect);
            }
        }

        protected override void OnInAirPackets(Point[] pLocations)
        {
            // save the first packet of the locations
            int posX = pLocations[0].X;
            int posY = pLocations[0].Y;

            // save the selection box
            Rectangle selectionRect = Woz.ScreenManager.Instance.SelectedScreen.SelectedEntitiesBoundingBox;

            // clear the scale direction
            this.mMotionType = MotionType.None;

            // check if there is a selection box
            if (selectionRect != Rectangle.Empty)
            {
                // get the bounds of the selection box
                int left = selectionRect.Left;
                int right = selectionRect.Right;
                int top = selectionRect.Top;
                int bottom = selectionRect.Bottom;

                // calculate the world dimensions of the following pixel coordinates
                int fivePixels = Woz.InkSpaceConverter.Instance.InkSpaceFivePixels;

                // check if the stylus is hovering over the top left corner of the selection box
                if ((posX >= (left - fivePixels)) && (posX <= (left + fivePixels)) &&
                    (posY >= (top - fivePixels)) && (posY <= (top + fivePixels)))
                {
                    this.mMotionType = MotionType.ScaleTopLeft;
                    base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors[this.mHasLockedEntity ? "invalid" : "sizenwse"]);
                    //base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors["invalid"]);
                    return;
                }

                // check if the stylus is hovering over the top right corner of the selection box
                else if ((posX >= (right - fivePixels)) && (posX <= (right + fivePixels)) &&
                    (posY >= (top - fivePixels)) && (posY <= (top + fivePixels)))
                {
                    this.mMotionType = MotionType.ScaleTopRight;
                    base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors[this.mHasLockedEntity ? "invalid" : "sizenesw"]);
                    //base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors["invalid"]);
                    return;
                }

                // check if the stylus is hovering over the bottom left corner of the selection box
                else if ((posX >= (left - fivePixels)) && (posX <= (left + fivePixels)) &&
                    (posY >= (bottom - fivePixels)) && (posY <= (bottom + fivePixels)))
                {
                    this.mMotionType = MotionType.ScaleBottomLeft;
                    base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors[this.mHasLockedEntity ? "invalid" : "sizenesw"]);
                    //base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors["invalid"]);
                    return;
                }

                // check if the stylus is hovering over the bottom right corner of the selection box
                else if ((posX >= (right - fivePixels)) && (posX <= (right + fivePixels)) &&
                    (posY >= (bottom - fivePixels)) && (posY <= (bottom + fivePixels)))
                {
                    this.mMotionType = MotionType.ScaleBottomRight;
                    base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors[this.mHasLockedEntity ? "invalid" : "sizenwse"]);
                    //base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors["invalid"]);
                    return;
                }

                // check if the stylus is hovering over the selection at all
                else if ((posX >= left) && (posX <= right) && (posY >= top) && (posY <= bottom))
                {
                    this.mMotionType = MotionType.Movement;
                    base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors[this.mHasLockedEntity ? "invalid" : "sizeall"]);
                    return;
                }
            }

            // check if the stylus is hovering over an unselected entity
            if (Woz.DynamicVariables.Instance.AlwaysPerformHitTest)
            {
                Entity hitEntity = Woz.ScreenManager.Instance.SelectedScreen.HitTest(pLocations[0]);
                if (hitEntity != null)
                {
                    base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors[hitEntity.Locked ? "invalid" : "sizeall"]);
                    return;
                }
            }

            // if all else fails then reset the cursor back to an arrow
            base.SetCurrentCursor(Woz.ResourceManager.Instance.Cursors["arrow"]);
        }

        protected override void OnStylusUp(Point pLocation)
        {
            // check if this was a movement or not
            if (this.mStylusMoved)
            {
                // save the selection rectangle
                Rectangle selectionRect = this.mSelectionRect;

                // something must have been selected so commit the select action
                if (this.mIsDrawingSelectionBox)
                {
                    // stop drawing selection box
                    this.mIsDrawingSelectionBox = false;

                    // see if anything was selected with this selection rectangle
                    Woz.Entity[] entities = Woz.ScreenManager.Instance.SelectedScreen.HitTest(selectionRect,
                        Woz.StaticVariables.Instance.SelectionPercentage);

                    // now create a selection action out of it and execute it
                    Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.SelectAction),
                        Woz.ScreenManager.Instance.SelectedScreen,
                        Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities,
                        entities);
                    action.Execute();

                    // inflate the invalid rectangle by twelve pixels to account for the selection box
                    selectionRect.Inflate(Woz.InkSpaceConverter.Instance.InkSpaceTenPixels, Woz.InkSpaceConverter.Instance.InkSpaceTenPixels);
                    selectionRect.Inflate(Woz.InkSpaceConverter.Instance.InkSpaceTwoPixels, Woz.InkSpaceConverter.Instance.InkSpaceTwoPixels);

                    // invalidate the selection rectangle
                    base.InvalidateRect(selectionRect);
                }

                // something must have been moved so commit the move action
                else
                {
                    // get the selected entities
                    Woz.Entity[] selectedEntities = Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities;

                    // compute the change in the amount of movement
                    Rectangle oldSelectionRect = this.mOriginalSelectedEntitiesRect;
                    selectionRect = Woz.Entity.ComputeBoundingBox(selectedEntities);

                    // calculate the change in position of the selected entities
                    Point delta = new Point(selectionRect.X - oldSelectionRect.X, selectionRect.Y - oldSelectionRect.Y);

                    // move all the entities back to their original position
                    foreach (Woz.Entity entity in selectedEntities)
                    {
                        // only translate unlocked entities
                        if (!entity.Locked)
                        {
                            entity.Translate(-delta.X, -delta.Y);
                        }
                    }

                    // commit the action
                    Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.MoveAction), Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities, delta);
                    action.Execute();
                }
            }

             // this must have been a click so commit the select action
            else
            {
                // cancel the drawing of a selection box
                this.mIsDrawingSelectionBox = false;

                // the single entity that was selected by this click or null if nothing was selected
                Woz.Entity entity = Woz.ScreenManager.Instance.SelectedScreen.HitTest(pLocation);

                // make sure the entity is not already selected
                {
                    Woz.Entity[] selectedEntities = Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities;

                    // if the selected entities has exactly one entity 
                    if (selectedEntities.Length == 1)
                    {
                        // and that entity is the one that is about to be selected
                        if (selectedEntities[0] == entity)
                        {
                            // ignore it
                            return;
                        }
                    }
                }

                // create an array to pass to the select action
                List<Woz.Entity> entities = new List<Woz.Entity>();

                // if there was an entity selected by this click
                if (entity != null)
                {
                    // add it to the entities array
                    entities.Add(entity);
                }

                // now create a selection action out of it and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.SelectAction),
                    Woz.ScreenManager.Instance.SelectedScreen,
                    Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities,
                    entities.ToArray());
                action.Execute();
            }
        }

        /// <summary>
        /// draws the selections of current screen on the given graphics object
        /// </summary>
        /// <param name="pGraphics">graphics object to draw the selections on</param>
        public override void Draw(Graphics pGraphics)
        {
            // set the smoothing mode to default so the selection lines are not antialiased
            pGraphics.SmoothingMode = SmoothingMode.Default;

            // initialize the inkspace sizes
            int tenPixels = Woz.InkSpaceConverter.Instance.InkSpaceTenPixels;
            int fivePixels = Woz.InkSpaceConverter.Instance.InkSpaceFivePixels;

            Color gradientStart = Color.FromArgb(231, 235, 255);
            Color gradientEnd = Color.FromArgb(180, 207, 248);

            // update the dash pattern to have five pixels by four pixels
            this.mSelectionPen.DashPattern = new float[] { (float)fivePixels, (float)fivePixels };

            // compute the bounding box of the selected entities
            Rectangle boundingBox = Woz.Entity.ComputeBoundingBox(Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities);

            // make sure the bounding box is not empty
            if (boundingBox != Rectangle.Empty)
            {
                // draw the selections around selected entities
                pGraphics.DrawRectangle(this.mSelectionPen, boundingBox);

                // draw the four corners
                {
                    // calculate the the size of the corners
                    Size cornerSize = new Size(tenPixels, tenPixels);

                    // calculate the four corners
                    Point upperLeft = new Point(boundingBox.X - fivePixels, boundingBox.Y - fivePixels);
                    Point upperRight = new Point(boundingBox.X + boundingBox.Width - fivePixels, boundingBox.Y - fivePixels);
                    Point lowerLeft = new Point(boundingBox.X - fivePixels, boundingBox.Y + boundingBox.Height - fivePixels);
                    Point lowerRight = new Point(boundingBox.X + boundingBox.Width - fivePixels, boundingBox.Y + boundingBox.Height - fivePixels);

                    // upper left corner
                    Rectangle upperLeftRect = new Rectangle(upperLeft, cornerSize);
                    // liniar gradient
                    pGraphics.FillRectangle(
                        new LinearGradientBrush(
                            upperLeftRect,
                            gradientStart,
                            gradientEnd,
                            LinearGradientMode.ForwardDiagonal),
                        upperLeftRect);
                    pGraphics.DrawRectangle(this.mOutlinePen,
                        upperLeftRect);

                    // upper right corner
                    Rectangle upperRightRect = new Rectangle(upperRight, cornerSize);
                    pGraphics.FillRectangle(
                        new LinearGradientBrush(
                            upperRightRect,
                            gradientStart,
                            gradientEnd,
                            LinearGradientMode.ForwardDiagonal),
                        upperRightRect);
                    pGraphics.DrawRectangle(this.mOutlinePen,
                        upperRightRect);

                    // lower left corner
                    Rectangle lowerLeftRect = new Rectangle(lowerLeft, cornerSize);
                    pGraphics.FillRectangle(
                        new LinearGradientBrush(
                            lowerLeftRect,
                            gradientStart,
                            gradientEnd,
                            LinearGradientMode.ForwardDiagonal),
                        lowerLeftRect);
                    pGraphics.DrawRectangle(this.mOutlinePen,
                        lowerLeftRect);

                    // lower right corner
                    Rectangle lowerRightRect = new Rectangle(lowerRight, cornerSize);
                    pGraphics.FillRectangle(
                        new LinearGradientBrush(
                            lowerRightRect,
                            gradientStart,
                            gradientEnd,
                            LinearGradientMode.ForwardDiagonal),
                        lowerRightRect);
                    pGraphics.DrawRectangle(this.mOutlinePen,
                        lowerRightRect);
                }
            }

            // draw the selection box
            if (this.mIsDrawingSelectionBox)
            {
                pGraphics.DrawRectangle(this.mSelectionPen, this.mSelectionRect);
            }
        }

        private void RegisterCallbacks()
        {
            // subscribe to the selected screen update requested
            Woz.ScreenManager.Instance.SelectedScreenUpdateRequested += new ScreenManager.SelectedScreenChangeHandler(this.cScreenManagerHasSelectedScreenUpdateRequested);

            // cal the screen update requested for the first time
            this.cScreenManagerHasSelectedScreenUpdateRequested(this, Rectangle.Empty);

            // subscribe to the selected screen changed
            Woz.ScreenManager.Instance.ScreenSelected += new ScreenManager.ScreenHandler(this.cScreenManagerHasScreenSelected);

            // create an instance of the button handler delegate to subscribe all the interface buttons to
            Woz.Button.ButtonHandler buttonHandler = new Woz.Button.ButtonHandler(this.cInterfaceManagerButtonClicked);

            // subscribe to the cut button
            Woz.InterfaceManager.Instance.Buttons["Cut"].Click += buttonHandler;

            // subscribe to the copy button
            Woz.InterfaceManager.Instance.Buttons["Copy"].Click += buttonHandler;

            // subscribe to the clone button
            Woz.InterfaceManager.Instance.Buttons["Clone"].Click += buttonHandler;

            // subscribe to the paste button
            Woz.InterfaceManager.Instance.Buttons["Paste"].Click += buttonHandler;

            // subscribe to the paste in place button
            Woz.InterfaceManager.Instance.Buttons["Paste in Place"].Click += buttonHandler;

            // subscribe to the select all button
            Woz.InterfaceManager.Instance.Buttons["Select All"].Click += buttonHandler;

            // subscribe to the deselect all button
            Woz.InterfaceManager.Instance.Buttons["Deselect All"].Click += buttonHandler;

            // subscribe to the group button
            Woz.InterfaceManager.Instance.Buttons["Group"].Click += buttonHandler;

            // subscribe to the ungroup button
            Woz.InterfaceManager.Instance.Buttons["Ungroup"].Click += buttonHandler;

            // subscribe to the lock button
            Woz.InterfaceManager.Instance.Buttons["Lock"].Click += buttonHandler;

            // subscribe to the unlock button
            Woz.InterfaceManager.Instance.Buttons["Unlock"].Click += buttonHandler;

            // subscribe to the bring to front button
            Woz.InterfaceManager.Instance.Buttons["Bring To Front"].Click += buttonHandler;

            // subscribe to the bring forward button
            Woz.InterfaceManager.Instance.Buttons["Bring Forward"].Click += buttonHandler;

            // subscribe to the send backward button
            Woz.InterfaceManager.Instance.Buttons["Send Backward"].Click += buttonHandler;

            // subscribe to the send to back button
            Woz.InterfaceManager.Instance.Buttons["Send To Back"].Click += buttonHandler;
        }

        private void cScreenManagerHasScreenSelected(object pSender, Screen pScreen)
        {
            if (this.mCurrentScreen == pScreen)
            {
                return;
            }

            // deselect the entities in old screen
            if (this.mCurrentScreen.SelectedEntities.Length > 0)
            {
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.SelectAction),
                    this.mCurrentScreen, this.mCurrentScreen.SelectedEntities, new Woz.Entity[0]);
                action.Execute();
            }

            // change the current screen to new screen
            this.mCurrentScreen = pScreen;

            // change the selected screen
            Woz.ScreenManager.Instance.SelectedScreen = pScreen;
        }

        private void cInterfaceManagerButtonClicked(Button pButton)
        {
            if (pButton.Name == "Delete")
            {
                // create the delete action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.DeleteAction),
                    Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities);
                action.Execute();
            }
            else if (pButton.Name == "Clone")
            {
                // create the clone action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.CloneAction),
                    Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities);
                action.Execute();
            }
            else if (pButton.Name == "Cut")
            {
                // reset the consecutive paste
                Woz.DynamicVariables.Instance.ConsecutivePaste = 1;

                // clone the selected entities for copying
                Woz.ScreenManager.Instance.CopySelectedEntities();

                // create the delete action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.DeleteAction),
                    Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities);
                action.Execute();
            }
            else if (pButton.Name == "Copy")
            {
                // reset the consecutive paste
                Woz.DynamicVariables.Instance.ConsecutivePaste = 1;

                // clone the selected entities for copying
                Woz.ScreenManager.Instance.CopySelectedEntities();

                // now request the screen to be updated
                Woz.ScreenManager.Instance.RequestSelectedScreenUpdate(
                    Woz.ScreenManager.Instance.SelectedScreen,
                    Rectangle.Empty);
            }
            else if (pButton.Name == "Paste")
            {
                // create the paste action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.PasteAction),
                    false);
                action.Execute();
            }
            else if (pButton.Name == "Paste in Place")
            {
                // create the paste in place action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.PasteAction),
                    true);
                action.Execute();
            }
            else if (pButton.Name == "Select All")
            {
                // create the select all action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.SelectAction),
                    this.mCurrentScreen,
                    this.mCurrentScreen.SelectedEntities,
                    this.mCurrentScreen.Entities);
                action.Execute();

                // TODO: uncomment the following lines to debug selected entities
                //Console.WriteLine("--------------------------------RefCount--------------------------");
                //foreach (Woz.Entity entity in Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities)
                //{
                //   Console.WriteLine("{0} - refCount: {1}", entity.ToString(), entity.RefCount);
                //}
                //Console.WriteLine();
            }
            else if (pButton.Name == "Deselect All")
            {
                // create the deselect all action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.SelectAction),
                    this.mCurrentScreen,
                    this.mCurrentScreen.SelectedEntities,
                    new Woz.Entity[0]);
                action.Execute();
            }
            else if (pButton.Name == "Group")
            {
                // create the group action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.GroupAction),
                    this.mCurrentScreen.SelectedEntities);
                action.Execute();
            }
            else if (pButton.Name == "Ungroup")
            {
                // create a seperate ungroup action for each group that needs to be ungrouped
                foreach (Woz.Entity entity in this.mCurrentScreen.SelectedEntities)
                {
                    if (entity is Woz.Group)
                    {
                        // create the ungroup action and execute it
                        Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.UngroupAction),
                            entity as Woz.Group);
                        action.Execute();
                    }
                }
            }
            else if (pButton.Name == "Lock")
            {
                // create the lock action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.LockAction),
                    this.mCurrentScreen.SelectedEntities);
                action.Execute();
            }
            else if (pButton.Name == "Unlock")
            {
                // create the unlock action and execute it
                Woz.Action action = Woz.ActionManager.Instance.CreateAction(typeof(Woz.UnlockAction),
                    this.mCurrentScreen.SelectedEntities);
                action.Execute();
            }
            else if (pButton.Name == "Send To Back")
            {
            }
            else if (pButton.Name == "Send Backward")
            {
            }
            else if (pButton.Name == "Bring To Front")
            {
            }
            else if (pButton.Name == "Bring Forward")
            {
            }
        }

        private void cScreenManagerHasSelectedScreenUpdateRequested(object pSender, Rectangle pBoundingBox)
        {
            // save the selected entities
            Woz.Entity[] selectedEntities = Woz.ScreenManager.Instance.SelectedScreen.SelectedEntities;

            // save the screen entities
            Woz.Entity[] screenEntities = Woz.ScreenManager.Instance.SelectedScreen.Entities;

            // get whether or not there are selected entities
            bool entitiesSelected = (selectedEntities.Length != 0);

            // check the clipboard for woz data
            bool clipboardHasData = (Woz.ScreenManager.Instance.CopiedEntities != null);

            // check to see if the screen has any entities
            bool screenHasEntities = (screenEntities.Length != 0);

            // update the delete button
            Woz.InterfaceManager.Instance.Buttons["Delete"].Enabled = entitiesSelected;

            // update the copy button
            Woz.InterfaceManager.Instance.Buttons["Copy"].Enabled = entitiesSelected;

            // update the cut button
            Woz.InterfaceManager.Instance.Buttons["Cut"].Enabled = entitiesSelected;

            // update the clone button
            Woz.InterfaceManager.Instance.Buttons["Clone"].Enabled = entitiesSelected;

            // update the deselect all button
            Woz.InterfaceManager.Instance.Buttons["Deselect All"].Enabled = entitiesSelected;

            // update the paste button
            Woz.InterfaceManager.Instance.Buttons["Paste"].Enabled = clipboardHasData;

            // update the paste in place button
            Woz.InterfaceManager.Instance.Buttons["Paste in Place"].Enabled = clipboardHasData;

            // update the select all button
            Woz.InterfaceManager.Instance.Buttons["Select All"].Enabled = (selectedEntities.Length != screenEntities.Length);

            // update the send to back button
            Woz.InterfaceManager.Instance.Buttons["Send To Back"].Enabled = entitiesSelected;

            // update the send backward button
            Woz.InterfaceManager.Instance.Buttons["Send Backward"].Enabled = entitiesSelected;

            // update the bring forward button
            Woz.InterfaceManager.Instance.Buttons["Bring Forward"].Enabled = entitiesSelected;

            // update the bring to front button
            Woz.InterfaceManager.Instance.Buttons["Bring To Front"].Enabled = entitiesSelected;

            // update the group button
            Woz.InterfaceManager.Instance.Buttons["Group"].Enabled = (selectedEntities.Length > 1);

            // update the ungroup button
            Woz.InterfaceManager.Instance.Buttons["Ungroup"].Enabled = (selectedEntities.Length == 1 && selectedEntities[0] is Woz.Group);

            // see if there are locked or unlocked entities
            this.mHasLockedEntity = false;
            this.mHasUnlockedEntity = false;
            foreach (Woz.Entity entity in selectedEntities)
            {
                this.mHasLockedEntity = (this.mHasLockedEntity || entity.Locked);
                this.mHasUnlockedEntity = (this.mHasUnlockedEntity || !entity.Locked);
            }

            // update the lock button
            Woz.InterfaceManager.Instance.Buttons["Lock"].Enabled = this.mHasUnlockedEntity;

            // update the unlock button
            Woz.InterfaceManager.Instance.Buttons["Unlock"].Enabled = this.mHasLockedEntity;
        }

        /// <summary>
        /// the selection rectangle also called marquee
        /// </summary>
        private Rectangle mSelectionRect;

        /// <summary>
        /// the original selected rectangle
        /// </summary>
        private Rectangle mOriginalSelectedEntitiesRect;

        /// <summary>
        /// stores the type of motion
        /// </summary>
        private Woz.MotionType mMotionType;

        /// <summary>
        /// the starting point of the selection
        /// </summary>
        private Point mStartingPoint;

        /// <summary>
        /// the last point in the selections movement
        /// </summary>
        private Point mLastPoint;

        /// <summary>
        /// flag indicating whether or not the stylus moved
        /// </summary>
        private bool mStylusMoved;

        /// <summary>
        /// flag indicating whether or not the 
        /// </summary>
        private bool mIsDrawingSelectionBox;

        /// <summary>
        /// flag indicating whether the selection has a locked entity
        /// </summary>
        private bool mHasLockedEntity;

        /// <summary>
        /// flag indicating whether the selection has a unlocked entity
        /// </summary>
        private bool mHasUnlockedEntity;

        /// <summary>
        /// the selection pen used when drawing a selection marquee
        /// </summary>
        private Pen mSelectionPen;

        /// <summary>
        /// the outline pen used when drawing a selection outline around an entity
        /// </summary>
        private Pen mOutlinePen;

        /// <summary>
        /// the current screen that were performing selections on
        /// </summary>
        private Woz.Screen mCurrentScreen;
    }
}
